package 
{
	import de.polygonal.ds.Iterator;
	import de.polygonal.ds.SLinkedList;
	
	import flash.display.GradientType;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.filters.BlurFilter;
	import flash.filters.DropShadowFilter;
	import flash.geom.Matrix;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	
	import oli.plugmgr.core.PlugLoadQue;
	import oli.plugmgr.core.PlugRegistry;
	import oli.plugmgr.errors.PlugDependencyError;
	import oli.plugmgr.events.PlugRegEvent;
	import oli.plugpack.core.IPlugInActivator;
	import oli.plugpack.core.PlugContext;
	import oli.plugpack.core.PlugIn;
	import oli.plugpack.errors.PlugError;
	import oli.plugpack.events.PlugLoadEvent;

	public class PlugManager extends Sprite
	{
		
		// PlugManager Settings 
		//---------------------------------------------------------------------------------
		private const SETTINGS_FILE:String = 'plugmngr.settings';
		private var PLUG_LIST_PATH:String;
		public static var PLUGINS_PATH:String;
		private var SERVERSCRIPT:String;
		public var PLUG_DEPENDENCY_POLICY:String;
		public static var SECURITY_DOMAIN:String;
		public static var TOTAL_BYTES:Number = 0;
		//-----------------------------------------------------------------------------------
		private var currentPlugName:String;
		
		private var pq:PlugLoadQue;
		
		private var plugReg:PlugRegistry;
		
		private var preloader:Sprite;
		
		public function PlugManager()
		{
			//init();
		}
		public function init():void
		{
			// Simple preloader
			//todo create preloader class with kewl FX 
			
			preloader = new Sprite();
			var g:Graphics = preloader.graphics;
			g.beginGradientFill(GradientType.LINEAR,[0x9999aa, 0x223333],[.75,.75],[255,0],new Matrix() ,'pad', 'rgb' ,0);
			g.drawRoundRect(0,0,30,15,5,5);
			g.endFill();
			preloader.x = stage.stageWidth/2 - preloader.width/2;
			preloader.y = stage.stageHeight/2 - preloader.height/2;
			var tx:TextField = new TextField();
			tx.alpha = .75;
			tx.autoSize = TextFieldAutoSize.CENTER;
			tx.backgroundColor = 0x333333;
			tx.selectable = false;
			tx.thickness = .1;
			tx.textColor = 0xaaaaaa;
			tx.text = '0 %';
			tx.scaleX = tx.scaleY = 5;
			tx.filters = [new BlurFilter(2,2,1), new DropShadowFilter(3,75,0,.75)];
			tx.x = preloader.width/2 -tx.width/2;
			tx.y = preloader.height/2 -tx.height/2;
			preloader.addChild(tx);
			addChild(preloader);
			
			loadSettings(SETTINGS_FILE);
		}
		private function loadSettings(settingsFilePath:String):void
		{
			var settingsLoader:URLLoader = new URLLoader(new URLRequest(settingsFilePath));
			registerSettingsLoaderEventListeners(settingsLoader);
		}
		private function settingsLoaderHandler(e:Event):void
		{
			if(e.type == Event.COMPLETE)
			{
				unRegisterSettingsLoaderEventListeners(e.target as IEventDispatcher);
				var settings:XML = XML(e.target.data);
				
				PLUGINS_PATH = settings.pluginsPath;
				
				if(PLUGINS_PATH.charAt(PLUGINS_PATH.length-1) != '/' && PLUGINS_PATH.charAt(PLUGINS_PATH.length-1) != '\\')
				{
					PLUGINS_PATH += '/';
				}
				
				SERVERSCRIPT = settings.serverscript;
				
				PLUG_DEPENDENCY_POLICY = settings.plugDependencyPolicy;
				
				SECURITY_DOMAIN = settings.securityDomain;
				
				trace('Setting loaded. \n' + 
						PLUGINS_PATH +' | '+SERVERSCRIPT+' | '+PLUG_DEPENDENCY_POLICY+' | '+SECURITY_DOMAIN+'\n');
				 
				// settings loaded, start serverscript
				
				var scriptLoader:URLLoader = new URLLoader();
				var re:URLRequest = new URLRequest(SERVERSCRIPT);
				re.method = URLRequestMethod.POST;
				var pathVar:URLVariables = new URLVariables();
				pathVar.PLUGINS_PATH = PLUGINS_PATH;
				re.data = pathVar;
				registerScriptLoaderEventListeners(scriptLoader);
				scriptLoader.load(re);
				
				 
			}
			if(e.type == IOErrorEvent.IO_ERROR)
			{
				trace(e);
				throw new PlugError("PlugManager could not find '"+SETTINGS_FILE+"'. \n" + 
						"This file holds crucial information about the locations for necessary plugins and more. \n" + 
						"Make sure a file called '"+SETTINGS_FILE+"' lies next to PlugManager.swf. \n");
			}
			if(e.type == SecurityErrorEvent.SECURITY_ERROR)
			{
				trace(e);
				throw new PlugError("PlugManager has not access to '"+SETTINGS_FILE+"' due to Sandbox violations. \n");
			}
		}
		private function scriptLoaderHandler(e:Event):void
		{
			if(e.type == Event.COMPLETE)
			{
				unRegisterScriptLoaderEventListeners(e.target as IEventDispatcher);
				// script loaded... proceed with loadQue and set PLUGINS_PATH and figure out a way to pass totalBytes from PHP to Progressevent...
				
				var returnee:URLVariables = new URLVariables(String(e.target.data));
				
				// FIXME handle error from serverscript  
				
				TOTAL_BYTES = parseInt(returnee.ttlbyts) ;
				
				PLUG_LIST_PATH = returnee.plglst as String;
				
				
			//	trace(returnee.ttlbyts, TOTAL_BYTES, PLUG_LIST_PATH);
				
				pq = new PlugLoadQue();
				
				loadPlugList();
				
			}
			if(e.type == IOErrorEvent.IO_ERROR)
			{
				throw new PlugError('PlugManager could not find "'+SERVERSCRIPT+'" at the specified path in "plugmngr.settings". \n' );
				trace(e);
			}
			if(e.type == SecurityErrorEvent.SECURITY_ERROR)
			{
				throw new PlugError('PlugManager has no acces to "'+SERVERSCRIPT+'" due to sandbox violations. If "'+SERVERSCRIPT+'" lies on a \n' + 
						'different server, make sure you granted PlugManager access to that domain via a crosspolicy file. \n');
				trace(e);
			}
		}
		private function loadPlugList():void
		{
			var ul:URLLoader = new URLLoader(new URLRequest(PLUG_LIST_PATH));
			registerListLoaderEventListeners(ul);
		}
		private function listLoaderHandler(e:Event = null):void
		{
			if(e.type == Event.COMPLETE)
			{
				unRegisterListLoaderEventListeners(e.target as IEventDispatcher);
				
				var list:XML = XML(e.target.data);
				var lList:SLinkedList = new SLinkedList();
				
				for each (var path:String in list.plugin.@path)
				{
					//trace(pathName);
					lList.append(path);
				}
			//	trace(lList.getIterator());
				pq.load(lList);
				pq.addEventListener(PlugLoadEvent.PROGRESS, pqHandler, false, 0,true);
				pq.addEventListener(PlugLoadEvent.ALL_COMPLETE, pqHandler, false, 0, true);
				
			}
			if (e.type == IOErrorEvent.IO_ERROR)
			{
				var massage:String = "PlugManager could not load 'plugins.xml', which holds the information about the plugins " + 
						"locations.\n 'plugins.xml' is created dynamically on the server by '"+SERVERSCRIPT+"'. Possibly an error on the " + 
						"server occured, \n which prevented the creation of 'plugins.xml'. If this error keeps reoccuring, it is most likely " + 
						"a bug in the PHP script \n or some faulty setting in the 'php.ini' file on your server. /n";
				
				throw new PlugError(massage);
			}
			if (e.type == SecurityErrorEvent.SECURITY_ERROR)
			{
				trace(e);
				throw new PlugError("PlugManager has no access to 'plugins.xml' ,which holds information about the plugins locations,  \n" + 
						"due to Sandbox violations. ");
			}
		}
		private function pqHandler(p:PlugLoadEvent):void
		{
			if (p.type == PlugLoadEvent.PROGRESS)
			{
				// show preloading biatch
				var l:Number = Math.floor((p.bytesLoaded/TOTAL_BYTES)*100);
				var ttxx:TextField = preloader.getChildAt(0) as TextField;
				ttxx.text = l.toString() + ' %' ;
				//trace(ttxx.text, p);
			}
			else
			{
				// romove preloader
				removeChild(preloader);
				preloader = null;
			
				pq.removeEventListener(PlugLoadEvent.ALL_COMPLETE, pqHandler);
				pq = null;
				
				var plugins:Vector.<PlugIn> = p.plugins;
				
				// register plugins with meta in PlugRegistry
				plugReg = new PlugRegistry();
				plugReg.addEventListener(PlugRegEvent.PLUGREG, plugRegHandler, false, 0, true);
				
				for(var j:uint = 0; j< plugins.length; j++)
				{
					plugReg.registerPlugIn(plugins[j]);
					
					
				}
				for(var n:uint = 0; n< plugins.length; n++)
				{
					var idList:SLinkedList = plugReg.idList;
					var versionList:SLinkedList = plugReg.versionList;
					
					var registeredPlugs:Vector.<PlugIn> = plugReg.registry;
					var currentPlug:PlugIn = registeredPlugs[n];
					var depLen:uint = currentPlug.dependencies.length;
					var m:uint =0;
					
					for(;m < depLen; m++ )
					{
						var currentDep:String = currentPlug.dependencies[m];
						var currentVersion:uint = currentPlug.dependenciesVersions[m];
						
						// check in PLugRegistry for dep and rigth version
						try
						{
							var currentActivator:IPlugInActivator = currentPlug.reference;
							
							var depIt:Iterator = idList.getIterator();
							var versionIt:Iterator = versionList.getIterator();
							depIt.start();
							versionIt.start();
							while(depIt.hasNext())
							{
								if(currentDep === depIt.next())
								{
									// dependency found...check version
									var regVersion:uint = versionIt.next();
									
									if(currentVersion == regVersion)
									{
										trace("The required version ["+currentVersion+"] of '"+currentDep+"' is correct. " + 
												"Dependencies for plugin '"+currentPlug.name+"' ("+currentPlug.id+"). \n");
										
										
									}
									else if(currentVersion > regVersion)
									{
										var mess:String = "The required version ["+currentVersion+"] of '"+currentDep+"' is newer than registered version ["+regVersion+"]. " + 
												"Dependencies for plugin '"+currentPlug.name+"' ("+currentPlug.id+"). \n";
										
										trace(mess);
										
										if(PLUG_DEPENDENCY_POLICY != 'old' && PLUG_DEPENDENCY_POLICY != 'all')
										{
											throw new PlugDependencyError(mess);
										}
										
									}
									else
									{
										var mess2:String = "The required version ["+currentVersion+"] of '"+currentDep+"' is older than registered version ["+regVersion+"]. " + 
												"Dependencies for plugin '"+currentPlug.name+"' ("+currentPlug.id+"). \n";
										
										trace(mess2);
										
										if(PLUG_DEPENDENCY_POLICY != 'new' && PLUG_DEPENDENCY_POLICY != 'all')
										{
											trace(PLUG_DEPENDENCY_POLICY);
											throw new PlugDependencyError(mess2);
										}
										
									}
									
									// dependency found, no more need to loop 
									break;
								}
								else
								{
									versionIt.next();
								}
							}
	
							
							
						}
						catch (e:Error)
						{
							var origErr:String = e.getStackTrace();
							var message3 = "The plugin '"+currentPlug.name+"' depends on '"+currentDep+"["+currentVersion+"]' to function correctly. \nCheck " + 
									"its existence. \n"+origErr;
							throw new PlugDependencyError(message3+origErr);
						}
					}
				
					// all dependencies are good ,resume instanciation of plugins
					currentActivator.start(new PlugContext(currentPlug));
				
					addChild(currentActivator as Sprite);
					
					trace("adding"+currentActivator+"to displayList. \n");
				
				
				}	
			}	
			
		}
		private function plugRegHandler(p:PlugRegEvent):void
		{
			trace("Plugin: "+p.plugin.id+" registered. \n");
		}

		private function registerListLoaderEventListeners(i:IEventDispatcher):void
		{
			i.addEventListener(Event.COMPLETE, listLoaderHandler, false, 0, true);
			i.addEventListener(IOErrorEvent.IO_ERROR, listLoaderHandler, false, 0, true);
			i.addEventListener(SecurityErrorEvent.SECURITY_ERROR, listLoaderHandler, false, 0, true);
		}
		private function unRegisterListLoaderEventListeners(i:IEventDispatcher):void
		{
			i.removeEventListener(Event.COMPLETE, listLoaderHandler);
			i.removeEventListener(IOErrorEvent.IO_ERROR, listLoaderHandler);
			i.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, listLoaderHandler);
		}
		private function registerSettingsLoaderEventListeners(i:IEventDispatcher):void
		{
			i.addEventListener(Event.COMPLETE, settingsLoaderHandler, false, 0, true);
			i.addEventListener(IOErrorEvent.IO_ERROR, settingsLoaderHandler, false, 0, true);
			i.addEventListener(SecurityErrorEvent.SECURITY_ERROR, settingsLoaderHandler, false, 0, true);
		}
		private function unRegisterSettingsLoaderEventListeners(i:IEventDispatcher):void
		{
			i.removeEventListener(Event.COMPLETE, settingsLoaderHandler);
			i.removeEventListener(IOErrorEvent.IO_ERROR, settingsLoaderHandler);
			i.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, settingsLoaderHandler);
		}
		private function registerScriptLoaderEventListeners(i:IEventDispatcher):void
		{
			i.addEventListener(Event.COMPLETE, scriptLoaderHandler, false, 0, true);
			i.addEventListener(IOErrorEvent.IO_ERROR, scriptLoaderHandler, false, 0, true);
			i.addEventListener(SecurityErrorEvent.SECURITY_ERROR, scriptLoaderHandler, false, 0, true);
		}
		private function unRegisterScriptLoaderEventListeners(i:IEventDispatcher):void
		{
			i.removeEventListener(Event.COMPLETE, scriptLoaderHandler);
			i.removeEventListener(IOErrorEvent.IO_ERROR, scriptLoaderHandler);
			i.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, scriptLoaderHandler);
		}
	}
}